﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace Windows_AI_Assistant.Services
{
    [StructLayout(LayoutKind.Sequential)]
    public struct DLCreateP2spInfo
    {
        [MarshalAs(UnmanagedType.LPStr)]
        public string save_path;
        [MarshalAs(UnmanagedType.LPStr)]
        public string save_name;
        [MarshalAs(UnmanagedType.LPStr)]
        public string url;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct DLInitParam
    {
        [MarshalAs(UnmanagedType.LPStr)]
        public string app_id;
        [MarshalAs(UnmanagedType.LPStr)]
        public string app_version;
        [MarshalAs(UnmanagedType.LPStr)]
        public string cfg_path;
        public byte save_tasks;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct DLTaskState
    {
        public UInt64 speed;
        public UInt64 total_size;
        public UInt64 downloaded_size;
        public byte state_code;
        public UInt32 task_err_code;
        public UInt32 task_token_err;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct DLTrafficInfo
    {
        public UInt64 origin_size;
        public UInt64 p2p_size;
        public UInt64 p2s_size;
        public UInt64 dcdn_size;
    }

    public class XLDownloadAPI
    {
        const string DLL_NAME = "dk.dll";

        public Int32 Initialize(DLInitParam param)
        {
            return xl_dl_init(ref param);
        }

        public Int32 Uninit()
        {
            return xl_dl_uninit();
        }

        public (Int32, string) Login(string strLoginToken)
        {
            byte[] session = new byte[4096];

            Int32 ret = xl_dl_login(strLoginToken, session);

            if (ret != 0)
            {
                return (ret, "");
            }

            int length = 0;
            while (length < session.Length && session[length] != 0)
            {
                length++;
            }
            return (0, Encoding.UTF8.GetString(session, 0, length));
        }

        public Int32 SetConcurrentTaskCount(UInt32 count)
        {
            return xl_dl_set_concurrent_task_count(count);
        }

        public Int32 SetDownloadSpeedLimit(UInt32 speed)
        {
            return xl_dl_set_download_speed_limit(speed);
        }
        public Int32 SetUploadSwitch(UInt32 upload_switch)
        {
            return xl_dl_set_upload_switch(upload_switch);
        }

        public Int32 SetUploadSpeedLimit(UInt32 speed)
        {
            return xl_dl_set_upload_speed_limit(speed);
        }

        public (Int32, ulong[]) GetUnfinishedTasks()
        {
            UInt32 count = 0;
            ulong[] taskIDArray = null;
            Int32 ret = xl_dl_get_unfinished_tasks(taskIDArray, ref count);

            if (ret != 0)
            {
                return (ret, null);
            }

            taskIDArray = new ulong[count];
            ret = xl_dl_get_unfinished_tasks(taskIDArray, ref count);
            return (ret, taskIDArray);
        }

        public (Int32, ulong[]) GetFinishedTasks()
        {
            UInt32 count = 0;
            ulong[] taskIDArray = null;
            Int32 ret = xl_dl_get_finished_tasks(taskIDArray, ref count);

            if (ret != 0)
            {
                return (ret, null);
            }

            taskIDArray = new ulong[count];
            ret = xl_dl_get_finished_tasks(taskIDArray, ref count);
            return (ret, taskIDArray);
        }

        public (Int32, ulong) CreateP2SPTask(DLCreateP2spInfo info)
        {
            UInt64 taskID = 0;
            Int32 ret = xl_dl_create_p2sp_task(ref info, ref taskID);
            return (ret, taskID);
        }

        public Int32 SetTaskToken(ulong taskID, string taskToken)
        {
            Int32 ret = xl_dl_set_task_token(taskID, taskToken);
            return ret;
        }

        public Int32 StartTask(ulong taskID)
        {
            Int32 ret = xl_dl_start_task(taskID);
            return ret;
        }

        public Int32 StopTask(ulong taskID)
        {
            Int32 ret = xl_dl_stop_task(taskID);
            return ret;
        }

        public Int32 DeleteTask(ulong taskID, byte deleteFile)
        {
            Int32 ret = xl_dl_delete_task(taskID, deleteFile);
            return ret;
        }

        public (Int32, DLTaskState) GetTaskState(ulong taskID)
        {
            DLTaskState state = new DLTaskState();
            Int32 ret = xl_dl_get_task_state(taskID, ref state);
            return (ret, state);
        }

        public (Int32, string) GetTaskUrl(ulong taskID)
        {
            byte[] buff = null;
            UInt32 buff_len = 0;
            Int32 ret = xl_dl_get_task_info(taskID, "url", buff, ref buff_len);
            if (ret != 0)
            {
                return (ret, "");
            }
            buff = new byte[buff_len];
            ret = xl_dl_get_task_info(taskID, "url", buff, ref buff_len);
            if (ret != 0)
            {
                return (ret, "");
            }
            return (ret, Encoding.UTF8.GetString(buff, 0, (int)buff_len));
        }

        public (Int32, string) GetTaskSaveName(ulong taskID)
        {
            byte[] buff = null;
            UInt32 buff_len = 0;
            Int32 ret = xl_dl_get_task_info(taskID, "save_name", buff, ref buff_len);
            if (ret != 0)
            {
                return (ret, "");
            }
            buff = new byte[buff_len];
            ret = xl_dl_get_task_info(taskID, "save_name", buff, ref buff_len);
            if (ret != 0)
            {
                return (ret, "");
            }
            return (ret, Encoding.UTF8.GetString(buff, 0, (int)buff_len));
        }

        //get task traffic
        public (Int32, DLTrafficInfo) GetTaskTraffic(ulong taskID)
        {
            DLTrafficInfo traffic = new DLTrafficInfo();
            uint size = (uint)Marshal.SizeOf(typeof(DLTrafficInfo));
            byte[] buff = new byte[size];
            Int32 ret = xl_dl_get_task_info(taskID, "traffic", buff, ref size);
            if (ret != 0)
            {
                return (ret, default(DLTrafficInfo));
            }

            GCHandle handle = GCHandle.Alloc(buff, GCHandleType.Pinned);
            try
            {
                traffic = (DLTrafficInfo)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(DLTrafficInfo));
            }
            finally
            {
                handle.Free();
            }
            return (ret, traffic);
        }

        //get version
        public (Int32, string) GetVersion()
        {
            byte[] buff = null;
            UInt32 buff_len = 0;
            Int32 ret = xl_dl_version(buff, ref buff_len);
            if (ret != 0)
            {
                return (ret, "");
            }
            buff = new byte[buff_len];
            ret = xl_dl_version(buff, ref buff_len);
            if (ret != 0)
            {
                return (ret, "");
            }
            return (ret, Encoding.UTF8.GetString(buff, 0, (int)buff_len));
        }

        [DllImport(DLL_NAME)]
        private static extern Int32 xl_dl_init(ref DLInitParam param);

        [DllImport(DLL_NAME)]
        private static extern Int32 xl_dl_uninit();

        [DllImport(DLL_NAME)]
        private static extern Int32 xl_dl_login([MarshalAs(UnmanagedType.LPStr)] string login_token, [MarshalAs(UnmanagedType.LPArray)] byte[] session);

        [DllImport(DLL_NAME)]
        private static extern Int32 xl_dl_get_unfinished_tasks([MarshalAs(UnmanagedType.LPArray)] UInt64[] task_id_array, ref UInt32 count);

        [DllImport(DLL_NAME)]
        private static extern Int32 xl_dl_get_finished_tasks([MarshalAs(UnmanagedType.LPArray)] UInt64[] task_id_array, ref UInt32 count);

        [DllImport(DLL_NAME)]
        private static extern Int32 xl_dl_create_p2sp_task(ref DLCreateP2spInfo create_info, ref UInt64 task_id);

        [DllImport(DLL_NAME)]
        private static extern Int32 xl_dl_set_task_token(UInt64 task_id, [MarshalAs(UnmanagedType.LPStr)] string task_token);

        [DllImport(DLL_NAME)]
        private static extern Int32 xl_dl_start_task(UInt64 task_id);

        [DllImport(DLL_NAME)]
        private static extern Int32 xl_dl_stop_task(UInt64 task_id);

        [DllImport(DLL_NAME)]
        private static extern Int32 xl_dl_delete_task(UInt64 task_id, byte delete_file);

        [DllImport(DLL_NAME)]
        private static extern Int32 xl_dl_get_task_state(UInt64 task_id, ref DLTaskState state);

        //supported info_name: "url", "save_path", "save_name", "traffic"
        [DllImport(DLL_NAME)]
        private static extern Int32 xl_dl_get_task_info(UInt64 task_id, [MarshalAs(UnmanagedType.LPStr)] string info_name, [MarshalAs(UnmanagedType.LPArray)] byte[] buff, ref UInt32 buff_len);

        [DllImport(DLL_NAME)]
        private static extern Int32 xl_dl_set_concurrent_task_count(UInt32 count);

        [DllImport(DLL_NAME)]
        private static extern Int32 xl_dl_set_download_speed_limit(UInt32 speed);

        [DllImport(DLL_NAME)]
        private static extern Int32 xl_dl_set_upload_switch(UInt32 upload_switch);

        [DllImport(DLL_NAME)]
        private static extern Int32 xl_dl_set_upload_speed_limit(UInt32 speed);

        [DllImport(DLL_NAME)]
        private static extern Int32 xl_dl_version([MarshalAs(UnmanagedType.LPArray)] byte[] buff, ref UInt32 buff_len);

    }
}
